package common

import (
	"bytes"
	"crypto/md5"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	myzap "hardware_test/utils/zap"
	"io"
	"io/ioutil"
	mrand "math/rand"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/gin-gonic/gin"
)

var (
	Tick uint64
)

var (
	AppVersion string
	BuildDate  string
)

func GetVersion() string {
	return AppVersion
}

func GetBuildDate() string {
	return BuildDate
}

func GetTick() uint64 {
	Tick += 1
	return Tick
}

func GetTime() time.Time {
	return time.Now()
}

func GetTimeStampS() int64 {
	return time.Now().Unix()
}

func GetTimeStampMs() int64 {
	return time.Now().UnixNano() / 1e6
}

func GetTimeStampNs() int64 {
	return time.Now().UnixNano()
}

func Empty(val interface{}) bool {
	if val == nil {
		return true
	}
	v := reflect.ValueOf(val)

	switch v.Kind() {
	case reflect.String, reflect.Array:
		return v.Len() == 0
	case reflect.Map, reflect.Slice:
		return v.Len() == 0 || v.IsNil()
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	}
	return reflect.DeepEqual(val, reflect.Zero(v.Type()).Interface())
}

func MicrosecondsStr(elapsed time.Duration) string {
	return fmt.Sprintf("%.3fms", float64(elapsed.Nanoseconds())/1e6)
}

func RandomNumber(length int) string {
	table := [...]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
	b := make([]byte, length)
	n, err := io.ReadAtLeast(rand.Reader, b, length)
	if n != length {
		panic(err)
	}
	for i := 0; i < len(b); i++ {
		b[i] = table[int(b[i])%len(table)]
	}
	return string(b)
}

func FirstElement(args []string) string {
	if len(args) > 0 {
		return args[0]
	}
	return ""
}

func RandomString(length int) string {
	mrand.Seed(time.Now().UnixNano())
	var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
	b := make([]rune, length)
	for i := range b {
		b[i] = letters[mrand.Intn(len(letters))]
	}
	return string(b)
}

/// 文件操作

// IsDir 检测路径是否为文件夹
func IsDir(path string) bool {
	if info, err := os.Stat(path); err == nil {
		return info.IsDir()
	}

	return false
}

// FileIsExisted 检测文件是否存在
func FileIsExisted(path string) bool {
	if _, err := os.Stat(path); os.IsNotExist(err) {
		return false
	}

	return true
}

func MakeDir(dir string) error {
	if !FileIsExisted(dir) {
		if err := os.MkdirAll(dir, 0777); err != nil { //os.ModePerm
			fmt.Println("MakeDir failed:", err)
			return err
		}
	}
	return nil
}

// CopyFile 使用io.Copy
func CopyFile(src, des string) (written int64, err error) {
	srcFile, err := os.Open(src)
	if err != nil {
		return 0, err
	}
	defer srcFile.Close()

	//获取源文件的权限
	fi, _ := srcFile.Stat()
	perm := fi.Mode()

	//desFile, err := os.Create(des)  //无法复制源文件的所有权限
	desFile, err := os.OpenFile(des, os.O_RDWR|os.O_CREATE|os.O_TRUNC, perm) //复制源文件的所有权限
	if err != nil {
		return 0, err
	}
	defer desFile.Close()

	return io.Copy(desFile, srcFile)
}

// CopyFile2 使用ioutil.WriteFile()和ioutil.ReadFile()
func CopyFile2(src, des string) (written int64, err error) {
	//获取源文件的权限
	srcFile, err := os.Open(src)
	if err != nil {
		return 0, err
	}
	fi, _ := srcFile.Stat()
	perm := fi.Mode()
	srcFile.Close()

	input, err := ioutil.ReadFile(src)
	if err != nil {
		return 0, err
	}

	err = ioutil.WriteFile(des, input, perm)
	if err != nil {
		return 0, err
	}

	return int64(len(input)), nil
}

// CopyFile3 使用os.Read()和os.Write()
func CopyFile3(src, des string, bufSize int) (written int64, err error) {
	if bufSize <= 0 {
		bufSize = 1 * 1024 * 1024 //1M
	}
	buf := make([]byte, bufSize)

	srcFile, err := os.Open(src)
	if err != nil {
		return 0, err
	}
	defer srcFile.Close()

	//获取源文件的权限
	fi, _ := srcFile.Stat()
	perm := fi.Mode()

	desFile, err := os.OpenFile(des, os.O_CREATE|os.O_RDWR|os.O_TRUNC, perm)
	if err != nil {
		return 0, err
	}
	defer desFile.Close()

	count := 0
	for {
		n, err := srcFile.Read(buf)
		if err != nil && err != io.EOF {
			return 0, err
		}

		if n == 0 {
			break
		}

		if wn, err := desFile.Write(buf[:n]); err != nil {
			return 0, err
		} else {
			count += wn
		}
	}

	return int64(count), nil
}

func WriteFile(filename string, data []byte) error {
	// 创建或打开文件
	file, err := os.Create(filename)
	if err != nil {
		return fmt.Errorf("file create failed: %w", err)
	}
	defer file.Close() // 确保在函数结束时关闭文件

	// 写入字节数组到文件
	_, err = file.Write(data)
	if err != nil {
		return fmt.Errorf("file write failed: %w", err)
	}

	return nil
}

// GetAllFile 获取指定目录下所有文件，只有文件
func GetAllFile(pathname string, s []string) ([]string, error) {
	rd, err := ioutil.ReadDir(pathname)
	if err != nil {
		fmt.Println("read dir fail:", err)
		return s, err
	}

	for _, fi := range rd {
		if !fi.IsDir() {
			fullName := pathname + "/" + fi.Name()
			s = append(s, fullName)
		}
	}
	return s, nil
}

// GetFilesAndDirs 返回指定路径下的文件和文件夹路径
func GetFilesAndDirs(path string) ([]string, error) {
	// 读取指定路径下的文件和文件夹
	lastChar := path[len(path)-1]
	if lastChar == '/' {
		path = path[:len(path)-1]
	}

	files, err := os.ReadDir(path)
	if err != nil {
		return nil, fmt.Errorf("读取目录失败: %v", err)
	}

	// 创建路径列表
	var paths []string
	for _, file := range files {
		paths = append(paths, path+"/"+file.Name())
	}

	return paths, nil
}

// GetFiles 获取指定目录下所有文件，包括文件夹及文件夹内文件
func GetFiles(root string, maxDepth int) ([]string, error) {
	var files []string
	err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			// 计算当前目录的深度
			depth := len(filepath.SplitList(path))
			rootDepth := len(filepath.SplitList(root))
			if depth-rootDepth > maxDepth {
				return filepath.SkipDir
			}
		} else {
			files = append(files, path)
		}
		return nil
	})
	return files, err
}

// GetFileAttr 获取文件属性
func GetFileAttr(file string) *syscall.Stat_t {
	finfo, _ := os.Stat(file)
	return finfo.Sys().(*syscall.Stat_t)
}

// GetFileCTime 获取创建时间
func GetFileCTime(file string) time.Time {
	return time.Unix(int64(GetFileAttr(file).Ctim.Sec), 0)
}

// GetFileMTime 获取修改时间
func GetFileMTime(file string) time.Time {
	return time.Unix(int64(GetFileAttr(file).Mtim.Sec), 0)
}

// GetFileATime 获取访问时间
func GetFileATime(file string) time.Time {
	return time.Unix(int64(GetFileAttr(file).Atim.Sec), 0)
}

// DelFile 删除文件
func DelFile(file string) error {
	return os.Remove(file)
}

// DelDir 删除文件夹
func DelDir(dir string) error {
	return os.RemoveAll(dir)
}

func PrintHttpPost(c *gin.Context) {
	body, _ := io.ReadAll(c.Request.Body)
	myzap.Logger.Debugf("Body: %s", body)
	for key, val := range c.Request.Header {
		myzap.Logger.Debugf("Header: %s: %v", key, val)
	}
	// 需要重新新建一个 body，否则会造成后续无法使用 body,
	// 详见 http://razil.cc/post/2019/04/go_gin_middleware_request_body/
	c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
}

func PrintHttpGet(c *gin.Context) {
	for key, value := range c.Request.URL.Query() {
		myzap.Logger.Debugf("Query param: %s: %v", key, value)
	}
}

func GetMD5(str string) string {
	data := []byte(str)
	has := md5.Sum(data)
	md5str := fmt.Sprintf("%X", has)
	return md5str
}

func GetFile(file string) *os.File {
	f, e := os.Open(file)
	if e != nil {
		return nil
	}
	defer f.Close()

	return f
}

func GetFileContext(file string) string {
	data, err := os.ReadFile(file)
	if err != nil {
		myzap.Logger.Errorf("Read file fail: %v", err)
		return ""
	}

	return string(data)
}

func CalculateMD5(filePath string) string {
	// 打开文件
	file, err := os.Open(filePath)
	if err != nil {
		return ""
	}
	defer file.Close()

	hash := md5.New()
	if _, err := io.Copy(hash, file); err != nil {
		return ""
	}

	hashInBytes := hash.Sum(nil)
	md5String := hex.EncodeToString(hashInBytes)
	return md5String
}

// GetStringI2NthSpace 获取第n个空格后的字符串
func GetStringI2NthSpace(input string, i int, n int) string {
	parts := strings.Fields(input)
	if i < 1 || n > len(parts) {
		return ""
	}

	// 返回第n个空格后的字符串
	return strings.Join(parts[i:n], " ")
}

func GetStringAfterNthSpace(input string, n int) string {
	parts := strings.Fields(input)
	if n < 1 || n > len(parts) {
		return ""
	}

	// 返回第n个空格后的字符串
	return strings.Join(parts[n:], " ")
}

func ProcessIsExist(processName string) bool {
	// 执行 ps 命令
	cmd := exec.Command("ps", "-e")
	output, err := cmd.Output()
	if err != nil {
		return false // 返回错误
	}

	// 使用 grep 检查进程名称
	if strings.Contains(string(output), processName) {
		return true // 找到进程
	}
	return false // 未找到进程
}

func ProcessIsExistByPid(pid int) bool {
	// 查看 /proc 目录下是否有对应的 pid 目录
	_, err := os.ReadDir("/proc/" + strconv.Itoa(pid))
	return err == nil // 如果存在，说明进程存在
}

func KillProcess(processName string) bool {
	// 执行 ps 命令
	cmd := exec.Command("killall", processName)
	output, err := cmd.Output()
	myzap.Logger.Debugf("Process killall: %v, %v", err, string(output))
	if err != nil {
		return false
	}

	return true
}

func SetSysDate(time string) bool {
	cmd := exec.Command("date", "-s", time)
	output, err := cmd.Output()
	if err != nil {
		myzap.Logger.Errorf("set sys time: %s, err: %v", output, err)
		return false
	}

	myzap.Logger.Debugf("set sys time: %s", output)
	return true
}

func SyncRTC() bool {
	cmd := exec.Command("hwclock", "-wu")
	output, err := cmd.Output()
	if err != nil {
		myzap.Logger.Errorf("hwclock -wu, out: %v err: %v", output, err)
		return false
	}

	myzap.Logger.Debugf("hwclock -wu, : %s", output)
	return true
}
